`timescale 1ns/100ps

`include "sim_glb.sv"
`include "public_head.sv"

module tc;

localparam          CLK_PRD                 = 5;

reg                                         rst_n;
reg                                         clk;
reg                                         cke;

localparam          LP1_DW                  = 16;
localparam          LP1_FW                  = 12;
localparam          LP1_OACC_FW             = 24;   // must OACC_FW<=IN_FW+KI_MAX
localparam          LP1_PINC_DW             = 20;   // must PINC_DW<OUT_DW-OUT_FW+OACC_FW, and PINC_DW<=OACC_FW+IN_DW-IN_FW-KP_MIN
localparam          LP1_IINC_DW             = 11;   // must IINC_DW<OUT_DW-OUT_FW+OACC_FW, IINC_DW means accelerated speed, must be tiny
localparam          LP1_IACC_FW             = 24;   // must OACC_FW<=IACC_FW<=IN_FW+KI_MAX
localparam          LP1_KP_MAX              = 15;
localparam          LP1_KP_MIN              = 8;
localparam          LP1_KI_MAX              = 23;   // must KI_MAX>=KP_MAX
localparam          LP1_KI_MIN              = 15;

localparam          LP1_KPW                 = $clog2(LP1_KP_MAX+1); // range [KP_MIN:KP_MAX]
localparam          LP1_KIW                 = $clog2(LP1_KI_MAX+1); // range [KI_MIN:KI_MAX]


reg                                         lp1_vld;
wire        signed  [LP1_DW  :0]            lp1_err;
reg         signed  [LP1_DW-1:0]            lp1_sp;
wire        signed  [LP1_DW-1:0]            lp1_pv;

reg                                         cfg_clr;
reg                                         cfg_frz;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;
    cke=1'b1;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_pid_lp", 2);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:SRC
    integer i, j;

    lp1_vld = 1'b1;
    lp1_sp = 0;
    cfg_clr = 1'b0;
    cfg_frz = 1'b0;

    @(posedge rst_n);

    for (i=0; i<6; i=i+1) begin
        #200_000;
        lp1_sp = i*10000 - 30000;
    end

    #200_000;
    rgrs.one_chk_done("SRC is done.");
end

//  
//           2^(-kp)
//            |            /---<----\
//    in_dat  V            |        |      out_dat
// --------->(*)-(st)-[D]-(+)-(s)->[D]--(t)--------->
//      |    ______        ^      oacc_ff
//       -->|      |       |
// 2^(-ki)  |u_iacc|-------
// -------->|______|  sts_iacc tend to 0 after loop is stable
//           
pi_acc #(    // proportional and integral accumulator 
        .IN_DW                          (LP1_DW+1                       ),
        .IN_FW                          (LP1_FW                         ),
        .OUT_DW                         (LP1_DW                         ),
        .OUT_FW                         (LP1_FW                         ),	// must OUT_FW<=OACC_FW
        .OACC_FW                        (LP1_OACC_FW                    ),	// must OACC_FW<=IN_FW+KI_MAX
        .PINC_DW                        (LP1_PINC_DW                    ),	// must PINC_DW<OUT_DW-OUT_FW+OACC_FW, and PINC_DW<=OACC_FW+IN_DW-IN_FW-KP_MIN
        .IINC_DW                        (LP1_IINC_DW                    ),	// must IINC_DW<OUT_DW-OUT_FW+OACC_FW, IINC_DW means accelerated speed, must be tiny
        .IACC_FW                        (LP1_IACC_FW                    ),	// must OACC_FW<=IACC_FW<=IN_FW+KI_MAX
        .KP_MAX                         (LP1_KP_MAX                     ),
        .KP_MIN                         (LP1_KP_MIN                     ),
        .KI_MAX                         (LP1_KI_MAX                     ),	// must KI_MAX>=KP_MAX
        .KI_MIN                         (LP1_KI_MIN                     )
) u_pi_acc ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (cke                            ),

        .in_vld                         (lp1_vld                        ),
        .in_dat                         (lp1_err                        ),	// s(  IN_DW,   IN_FW)
        .out_dat                        (lp1_pv                         ),	// s( OUT_DW,  OUT_FW)

        .cfg_kp                         (4'd8                           ),	// range [KP_MIN:KP_MAX], gain 
        .cfg_ki                         (5'd12                          ),	// range [KI_MIN:KI_MAX], gain 

        .cfg_oacc_clr                   (cfg_clr                        ),
        .cfg_oacc_frz                   (cfg_frz                        ),
        .cfg_oacc_ini                   (16'd0                          ),	// s( OUT_DW,  OUT_FW)
        .sts_oacc_rtv                   (                               ),

        .cfg_iacc_clr                   (cfg_clr                        ),
        .cfg_iacc_frz                   (cfg_frz                        ),
        .cfg_iacc_ini                   ({LP1_IINC_DW{1'b0}}            ),	// s(IINC_DW, IINC_FW)
        .sts_iacc_rtv                   (                               )
);

assign lp1_err = lp1_sp - lp1_pv;

initial begin:DST

    @(posedge rst_n);
    

    rgrs.one_chk_done("DST is done.");
end


endmodule

